package com.zqh.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

/**
 * 聊天室服务端
 */
public class NioChatServer {

    // 服务器端启动
    public void startServer() throws IOException {

        // 创建 selector 对象
        Selector selector = Selector.open();

        // 创建 ServerSocketChannel 通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 为 channel 通道绑定监听端口
        serverSocketChannel.bind(new InetSocketAddress(8080));

        // 设置非阻塞模式
        serverSocketChannel.configureBlocking(false);

        // 把 channel 注册到 selector,
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动。。。");

        // 循环，等待新连接进入
        while (true) {

            // 获取 channel 数量
            int channelNum = selector.select();

            // 如果没有获取到 channel 则退出当前循环
            if(channelNum == 0) {
                continue;
            }

            // 获取可用的 channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 创建迭代器，遍历可用的 channel
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {

                SelectionKey selectionKey = iterator.next(); // 获取 channel ，判断其状态

                // 从集合中移除当前的 selectionKey (已经取出)
                iterator.remove();

                // 根据就绪状态，选择不同的业务
                // accept 状态
                if (selectionKey.isAcceptable()) {
                    acceptOperator(serverSocketChannel, selector);
                }
                // 可读状态
                if (selectionKey.isReadable()) {
                    readOperator(selector, selectionKey);
                }

            }

        }

    }

    // 处理可读状态的操作（当客户端发送消息时）
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {

        // 从 selectionKey 中获取已经就绪的通道
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();

        // 创建 buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        // 读取客户端的消息
        int read = socketChannel.read(byteBuffer);
        String message = "";
        if (read > 0) {
            byteBuffer.flip(); // 切换读模式
            message += Charset.forName("UTF-8").decode(byteBuffer);
        }

        // 将 channel 再次注册到选择器上，继续监听可读状态
        socketChannel.register(selector, SelectionKey.OP_READ);

        // 把客户端发送的消息广播到其他客户端
        if (message.length() > 0) {
            System.out.println(message);
            castOtherClient(message, selector, socketChannel);
        }

    }

    // 广播消息到其他客户端
    private void castOtherClient(String message, Selector selector, SocketChannel socketChannel) throws IOException {

        // 获取所有已经接入的客户端
        Set<SelectionKey> selectionKeySet = selector.keys(); // 获取所有 key 的集合

        // 循环所有 channel 并广播消息
        for (SelectionKey selectionKey : selectionKeySet) {
            // 获取每个 channel
            SelectableChannel targetChannel = selectionKey.channel();
            // 剔除自身体条件：必须是 SocketChannel 且不是自身
            if (targetChannel instanceof SocketChannel && targetChannel != socketChannel) {

                SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                if (message.contains("user")) {
                    // 用户上线通知
                    ((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(sdf.format(new Date()) + " 用户" + message.split(":")[1] + "上线了"));
                } else if (message.contains("exit")) {
                    // 用户下线通知
                    ((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(sdf.format(new Date()) + " 用户" + message.split(":")[0] + "下线了"));
                } else {
                    ((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(sdf.format(new Date()) + " " + message));
                }

            }
        }

    }

    // 处理接入状态操作（当客户端接入时）
    private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {

        // 创建 SocketChannel
        SocketChannel socketChannel = serverSocketChannel.accept();

        // 把 SocketChannel 设置为阻塞模式
        socketChannel.configureBlocking(false);

        // 将 channel 注册到 selector 选择器，监听可读状态
        socketChannel.register(selector, SelectionKey.OP_READ);

        // 给客户端回复消息
        socketChannel.write(Charset.forName("UTF-8").encode("欢迎进入聊天室！输入 exit 退出！"));

        // 广播上线通知
//        castOtherClient("")

    }



    public static void main(String[] args) {

        try {
            new NioChatServer().startServer();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}
